iT邦幫忙

2022 iThome 鐵人賽

0

今日目標,結算結果。

結算

這邊就只差最後一步了~~ /images/emoticon/emoticon18.gif

  1. 修改 GameStatus,加入 getAllPlayersHands()
    public HashMap<String, Object> getAllPlayersHands(String roomId) {
        HashMap<String, Object> result = new HashMap<>();
        Player[] players = this.game.get(roomId).getPlayers();
        for (Player player : players) {
            result.put(player.getName(), player.getHands());
        }
        return result;
    }
    
  2. 修改 GameService 的 play(),定義結束遊戲的訊息,函數修改後的完整內容為:
    public void play(String roomId) {
        Player[] players = gameStatus.getPlayers(roomId);
        int index = 0;
        for (int i=0; i<4; i++) {
            if (players[i].getName().equals(this.gameStatus.getCurrentPlayer(roomId))) {
                index = i;
                break;
            }
        }
    
        String currentPlayer;
        String previousPlayer;
        GameTimer timer = new GameTimer();
        this.gameTimerList.add(roomId, timer);
        try {
            Thread.sleep(5000);
        }
        catch(Exception e) {
            System.out.println(e.getMessage());
        }
    
        while (true) {
            previousPlayer = this.gameStatus.getPreviousPlayer(roomId);
            currentPlayer = players[index].getName();
            this.gameStatus.setCurrentPlayer(roomId, currentPlayer);
            if (currentPlayer.equals(previousPlayer)) {
                this.gameStatus.setPreviousPlayer(roomId, null);
                this.gameStatus.setPreviousPlayedCards(roomId, null);
            }
    
            timer.init(8);
            String finalCurrentPlayer = currentPlayer;
            String finalPreviousPlayer = previousPlayer;
            timer.countDown((n) -> {
                Map<String, Object> status = new HashMap<>();
                status.put("currentPlayer", finalCurrentPlayer);
                PlayedCards tmp = this.gameStatus.getPreviousPlayedCards(roomId);
                if (tmp == null) {
                    status.put("previousPlayedCards", null);
                }
                else {
                    status.put("previousPlayedCards", tmp.get());
                }
                status.put("timer", n);
                roomService.sendMessageToRoom(roomId, "/queue/game", status);
    
                if (n.equals("1")) {
                    boolean isFirstPlayer = this.findFirstPlayer(roomId) != null;
                    if (isFirstPlayer) {
                        ArrayList<Card> cards = new ArrayList<>();
                        cards.add(new Card(Suit.CLUB, Number.THREE));
                        this.autoPlay(roomId, finalCurrentPlayer, cards);
                        sendMyHands(finalCurrentPlayer);
                        sendHandsInfo(roomId);
                        return;
                    }
    
                    if (finalCurrentPlayer.equals(finalPreviousPlayer)) {
                        this.autoPlay(roomId, finalCurrentPlayer);
                        sendMyHands(finalCurrentPlayer);
                        sendHandsInfo(roomId);
                    }
                }
            });
            timer.await(25);
    
            if (gameStatus.getHandsByPlayerName(currentPlayer).size() == 0) {
                Map<String, Object> endMessage = new HashMap<>();
                endMessage.put("winner", currentPlayer);
                endMessage.put("hands", this.gameStatus.getAllPlayersHands(roomId));
                roomService.sendMessageToRoom(roomId, "/queue/end", endMessage);
    
                this.gameStatus.remove(roomId);
                this.gameTimerList.remove(roomId);
                for (Player player : players) {
                    this.userStatus.setUserReady(player.getName(), false);
                }
                return;
            }
            index = (index + 1) % 4;
        }
    }
    
  3. 修改 game.css,加入這段:
    .black-cover {
        position: absolute;
        background-color: #000;
        opacity: 0.5;
        height: 100%;
        width: 100%;
    }
    
    .end-window {
        position: absolute;
        margin: 65px 40px;
        height: 450px;
    }
    
    .end-hands .m-card {
        margin-top: 3px;
        height: 50px;
        width: 30px;
    }
    
    #button-end {
        position: absolute;
        top: 480px;
        left: 500px;
    }
    
  4. 修改 game.html,加入小視窗顯示的部分,修改後的完整內容為:
    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml"
        xmlns:th="http://www.thymeleaf.org"
        xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
        layout:decorate="~{layout.html}"
    >
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <div layout:fragment="content" class="card game-window">
            <input id="my-username" type="hidden" th:value="${username}">
    
            <div class="timer text-center">..</div>
    
            <div class="other-hands-90 other-hands-left" id="user-3"></div>
            <div class="other-hands" id="user-4"></div>
            <div class="my-hands" id="user-1"></div>
            <div class="other-hands-90 other-hands-right" id="user-2"></div>
    
            <div class="card-type"></div>
    
            <div class="action">
                <button type="button" class="btn btn-outline-success btn-lg" id="button-play" onclick="play()" disabled>出牌</button>
                <button type="button" class="btn btn-outline-danger btn-lg" id="button-pass" onclick="pass()" disabled>PASS</button>
            </div>
    
            <div class="black-cover d-none"></div>
            <div class="end-window card-deck d-none"></div>
        </div>
    
        <div layout:fragment="js-and-css">
            <!-- websockets -->
            <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
            <script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.js"
                    integrity="sha512-tL4PIUsPy+Rks1go4kQG8M8/ItpRMvKnbBjQm4d2DQnFwgcBYRRN00QdyQnWSCwNMsoY/MfJY8nHp2CzlNdtZA=="
                    crossorigin="anonymous"
                    referrerpolicy="no-referrer"></script>
    
            <!-- custom -->
            <script type="text/javascript" th:src="@{/js/WebSockets.js}"></script>
            <script type="text/javascript" th:src="@{/js/game.js}"></script>
            <link th:href="@{/css/game.css}" rel="stylesheet">
        </div>
    </body>
    </html>
    
  5. 修改 game.js,加入 subscribeEnd()endGame()
    function subscribeEnd() {
        websocket.subscribe("/user/queue/end", (response) => {
            response = JSON.parse(response.body);
            endGame(response);
        });
    }
    
    function endGame(data) {
        let winner = data.winner;
        delete data.hands[winner];
    
        // 產生 winner 的部分
        $(".end-window").empty();
        $(".end-window").append(`
            <div class="card" id="player-${winner}">
                <div class="card-body">
                    <h5 class="card-title text-center">${winner}</h5>
                    <br>
                    <img class="card-img-top" src="https://picsum.photos/200/200" alt="Card image cap">
                    <div class="end-hands">
                        <h3 class="text-danger text-center" style="margin-top: 10px;">Winner</h3>
                    </div>
                </div>
            </div>
        `)
    
        // 產生其他人的部分
        for (let name in data.hands) {
            $(".end-window").append(`
                <div class="card" id="player-${name}">
                    <div class="card-body">
                        <h5 class="card-title text-center">${name}</h5>
                        <br>
                        <img class="card-img-top" src="https://picsum.photos/200/200" alt="Card image cap">
                        <div class="end-hands"></div>
                    </div>
                </div>
            `);
            data.hands[name].sort(compareByGeneral);
            data.hands[name].map((card) => {
                card = convertCard(card);
                let tmp = card.split("-");
                let color = (tmp[0] == SUITS.SPADE || tmp[0] == SUITS.CLUB) ? "#000000" : "#FF0000";
                $(`#player-${name} .end-hands`).append(`
                    <div class="m-card text-center" style="color: ${color};">
                        <div>
                        ${tmp[0]}
                        <br>
                        ${tmp[1]}
                        </div>
                    </div>
                `)
            })
        }
    
        // 加入回到房間的按鈕
        $(".end-window").append(`
            <button type="button" class="btn btn-success btn-lg" id="button-end">回房間</button>
        `);
        $("#button-end").click(() => {
            let roomId = window.location.href.split("/").slice(-1)[0];
            window.location.href = `/room/${roomId}`;
        })
    
        // 顯示後面的黑色遮罩
        $(".black-cover").removeClass("d-none");
    
        // 顯示結算頁面
        $(".end-window").removeClass("d-none");
    }
    

心得

這次鐵人賽算是分了兩階段,第一階段是原本的專案開發,第二階段因為要寫文章,所以又開了新的專案在同步原本專案,剪剪貼貼難免會出問題,然後就花比較多的時間在 Debug... 如果還有哪裡有錯,還請各位大大不吝指教,我會盡快修復!
經過這次開發,我覺得以後不要再嘗試用網頁寫遊戲好了,這真的不是個好主意,雖然我在第一天就知道了... 不過在意料之外的是,我居然能連續寫完 30 天,畢竟我到第 10 天就開始覺得膩了,但還是硬著頭皮寫完,加上最近還有其他事情在忙,每天都在燃燒,不過完成鐵人賽還是滿有成就感的~~
小弟還留了些東西讓讀者自由發揮的空間,比如:房間和結算頁面的頭貼、錯誤訊息的反饋等,因為不是在這次功能裡特別重要的,所以就先跳過~~
最後,說下小弟在技術相關的疑問好了,希望有大大能解釋~~
1. Component 的實例,究竟還需不需要 Service 負責存取?
2. 不太確定為什麼每輪出牌都會有這麼高的延遲,是 WebSocket 的問題嗎?
/images/emoticon/emoticon07.gif


上一篇
Day 29 - 檢驗出牌
系列文
Spring Boot... 深不可測31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言